﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EODDataSync.EODDataService;
using Algobox.JuicyLib.MarketData;
using Algobox.JuicyLib.SQL.LiquidView;

namespace EODDataSync
{
    public class EODDataManager
    {
        public EODDataManager(string binding)
        {
            _soap = new EODDataService.DataSoapClient(binding);
            _loginToken = String.Empty;
            XLON_GBPSymbols = new List<string>();
            FOREX_Symbols = new List<string>();
            XLON_SymbolMap = new Dictionary<string, string>();
        }

        const int CCY_GBP = 1280;

        DataSoapClient _soap;

        string _loginToken;

        public Dictionary<string, string> XLON_SymbolMap { get; set; }
        public List<string> XLON_GBPSymbols { get; set; }
        public List<string> FOREX_Symbols { get; set; }

        
        public bool TryLogin(string username, string password)
        {
            try
            {
                LOGINRESPONSE response = _soap.Login(username, password);
                if (response != null)
                {
                    if (!String.IsNullOrEmpty(response.Token))
                    {
                        _loginToken = response.Token;
                        return true;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("TryUpdateNames caught an exception:{0}\n{1}", e.Message, e.StackTrace);
            }
            return false;
        }


        public bool TryUpdateNames(MarketDataConstants.ExchangeMICS mic)
        {
            try
            {
                string exchange = String.Empty;
                if (_loginToken != String.Empty && TryGetEODExchange(mic, out exchange))
                {
                    var response = _soap.SymbolList2(_loginToken, exchange);
                    if (response.SYMBOLS2 != null)
                    {
                        switch (mic)
                        {
                            case MarketDataConstants.ExchangeMICS.XNAS:
                            case MarketDataConstants.ExchangeMICS.XNYS:
                            case MarketDataConstants.ExchangeMICS.ARCX:
                                UpdateNamesUSCanada(mic.ToString(), "USD", response.SYMBOLS2);
                                break;
                            case MarketDataConstants.ExchangeMICS.XTSE:
                            case MarketDataConstants.ExchangeMICS.XTSX:
                                UpdateNamesUSCanada(mic.ToString(), "CAD", response.SYMBOLS2);
                                break;
                        }
                        return true;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("TryUpdateNames caught an exception:{0}\n{1}", e.Message, e.StackTrace);
            }
            return false;

        }

        private void UpdateNamesUSCanada(string mic, string currency, SYMBOL2[] symbols)
        {
            int retVal = -1;
            int? stockId;
            foreach (SYMBOL2 stock in symbols)
            {
#if !DEBUG
                LiquidViewHelper.TryUpdateInstrumentStockUSCanada(mic, currency, stock.c, stock.n, 2, out stockId, out retVal);
#endif
                if (retVal < 0)
                {
                    Console.WriteLine("Failed ({0}) to write {1} {2} {3} {4}", retVal, mic, currency, stock.c, stock.n);
                }
                else if (retVal == 0)
                {
                    Console.WriteLine("New " + mic + " stock " + stock.c + " created");
#if !DEBUG
                    LiquidViewHelper.LogException("EODDataSync", "New ARCX (AMEX) stock " + stock.c + " created");
#endif
                }
            }
        }


        public bool TryUpdateSplits(MarketDataConstants.ExchangeMICS mic)
        {
            string exchange = String.Empty;
            try
            {
                if (_loginToken != String.Empty && TryGetEODExchange(mic, out exchange))
                {
                    var response = _soap.SplitListByExchange(_loginToken, exchange);
                    if (response.SPLITS != null)
                    {
                        switch (mic)
                        {
                            case MarketDataConstants.ExchangeMICS.XNAS:
                            case MarketDataConstants.ExchangeMICS.XNYS:
                            case MarketDataConstants.ExchangeMICS.ARCX:
                                UpdateSplitsUS(mic.ToString(), response.SPLITS);
                                break;
                            case MarketDataConstants.ExchangeMICS.XTSE:
                            case MarketDataConstants.ExchangeMICS.XTSX:
                                UpdateSplitsCanada(response.SPLITS);
                                break;
                            case MarketDataConstants.ExchangeMICS.XLON:
                                UpdateSplitsUMTF('l', response.SPLITS);
                                break;
                            case MarketDataConstants.ExchangeMICS.XMIL:
                                UpdateSplitsUMTF('m', response.SPLITS);
                                break;
                        }
                        return true;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("TryUpdateSplits caught an exception:{0}\n{1}", e.Message, e.StackTrace);
            }
            return false;
        }


        private void UpdateSplitsUS(string exchange, SPLIT[] splits)
        {
            int retVal = -1;
            int recieves, from;
            foreach (SPLIT split in splits)
            {
                var stuff = split.Ratio.Split('-');
                if (Int32.TryParse(stuff[0], out recieves) && Int32.TryParse(stuff[1], out from))
                {
#if !DEBUG
                    LiquidViewHelper.TryImportSplitByUSSymbol(split.Symbol, exchange, split.DateTime, recieves, from, out retVal);
#endif
                    if (retVal == 0)
                        Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", split.DateTime.Date, split.Exchange, split.Symbol, split.Ratio);
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("Failed to parse split ratio");
                }
            }
        }


        private void UpdateSplitsCanada(SPLIT[] splits)
        {
            int retVal = -1;
            int recieves, from;
            foreach (SPLIT split in splits)
            {
                var stuff = split.Ratio.Split('-');
                if (Int32.TryParse(stuff[0], out recieves) && Int32.TryParse(stuff[1], out from))
                {
#if !DEBUG
                    LiquidViewHelper.TryImportSplitByCanadaSymbol(split.Symbol, split.DateTime.Date, recieves, from, out retVal);
#endif
                    if (retVal == 0)
                        Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", split.DateTime, split.Exchange, split.Symbol, split.Ratio);
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("Failed to parse split ratio");
                }
            }
        }

        private void UpdateSplitsUMTF(char umtfSuffix, SPLIT[] splits)
        {
            int retVal = -1;
            int recieves, from;
            string symbol;

            foreach (SPLIT split in splits)
            {
                var stuff = split.Ratio.Split('-');
                if (Int32.TryParse(stuff[0], out recieves) && Int32.TryParse(stuff[1], out from))
                {
                    if (umtfSuffix != 'l' || !XLON_SymbolMap.TryGetValue(split.Symbol, out symbol))
                        symbol = split.Symbol;

                    symbol += umtfSuffix;
#if !DEBUG
                    LiquidViewHelper.TryImportSplitByUMTF(symbol, split.DateTime.Date, recieves, from, out retVal);
#endif
                    if (retVal == 0)
                        Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", split.DateTime, split.Exchange, split.Symbol, split.Ratio);
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("Failed to parse split ratio");
                }
            }
        }




        public bool TryUpdateQuotes(MarketDataConstants.ExchangeMICS mic)
        {
            string exchange = String.Empty;
            try
            {
            if (_loginToken != String.Empty && TryGetEODExchange(mic, out exchange))
            {
                // for options just request specific instruments rather than the entire universe
                if (mic == MarketDataConstants.ExchangeMICS.XISX)
                {
                    UpdateQuotesUSOptions(mic.ToString(), exchange);
                    return true;
                }
                // for equities just download the whole file for each exchange
                else
                {
                    var response = _soap.QuoteList(_loginToken, exchange);
                    if (response.QUOTES != null)
                    {
                        switch (mic)
                        {
                            case MarketDataConstants.ExchangeMICS.XNAS:
                            case MarketDataConstants.ExchangeMICS.XNYS:
                            case MarketDataConstants.ExchangeMICS.ARCX:
                                UpdateQuotesUS(mic.ToString(), response.QUOTES);
                                break;
                            case MarketDataConstants.ExchangeMICS.XTSE:
                            case MarketDataConstants.ExchangeMICS.XTSX:
                                UpdateQuotesCanada(mic.ToString(), response.QUOTES);
                                break;
                            case MarketDataConstants.ExchangeMICS.XLON:
                                UpdateQuotesUMTF(mic.ToString(), 'l', response.QUOTES);
                                break;
                            case MarketDataConstants.ExchangeMICS.XMIL:
                                UpdateQuotesUMTF(mic.ToString(), 'm', response.QUOTES);
                                break;
                        }
                        return true;
                    }
                }
            }
            }
            catch (Exception e)
            {
                Console.WriteLine("TryUpdateQuotes for {2} caught an exception:{0}\n{1}", e.Message, e.StackTrace, mic);
            }
            return false;
        }


        public bool TryUpdateQuotes(MarketDataConstants.ExchangeMICS mic, DateTime date)
        {
            string exchange = String.Empty;
            try
            {
                if (_loginToken != String.Empty && TryGetEODExchange(mic, out exchange))
                {
                    // for options just request specific instruments rather than the entire universe
                    if (mic == MarketDataConstants.ExchangeMICS.XISX)
                    {
                        UpdateQuotesUSOptions(mic.ToString(), exchange, date);
                        return true;
                    }
                    // for equities just download the whole file for each exchange
                    else
                    {
                        var response = _soap.QuoteListByDate(_loginToken, exchange, date.ToString("yyyyMMdd"));

                        if (response.QUOTES != null)
                        {
                            switch (mic)
                            {
                                case MarketDataConstants.ExchangeMICS.XNAS:
                                case MarketDataConstants.ExchangeMICS.XNYS:
                                case MarketDataConstants.ExchangeMICS.ARCX:
                                    UpdateQuotesUS(mic.ToString(), response.QUOTES);
                                    break;
                                case MarketDataConstants.ExchangeMICS.XTSE:
                                case MarketDataConstants.ExchangeMICS.XTSX:
                                    UpdateQuotesCanada(mic.ToString(), response.QUOTES);
                                    break;
                                case MarketDataConstants.ExchangeMICS.XLON:
                                    UpdateQuotesUMTF(mic.ToString(), 'l', response.QUOTES);
                                    break;
                                case MarketDataConstants.ExchangeMICS.XMIL:
                                    UpdateQuotesUMTF(mic.ToString(), 'm', response.QUOTES);
                                    break;
                            }
                            return true;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("TryUpdateQuotes for {2} on {3:yyyyMMdd} caught an exception:{0}\n{1}", e.Message, e.StackTrace, mic, date);
            }
            return false;
        }


        private void UpdateQuotesUMTF(string exchange, char umtfSuffix, QUOTE[] quotes)
        {
            int retVal = -1;
            int currencyId;
            decimal multiplier;
            string symbol;

            // get stock currencies
            Dictionary<string, int> stocks = new Dictionary<string, int>();

            LiquidViewHelper.LiquidViewLinq.InstrumentsStocks.Where(a => a.MarketMIC == exchange && a.UMTF != null).ToList().ForEach(
                (stock) =>
                {
                    stocks[stock.UMTF] = stock.CurrencyId.Value;
                }
            );

            foreach (QUOTE quote in quotes)
            {
                if (umtfSuffix != 'l' || !XLON_SymbolMap.TryGetValue(quote.Symbol, out symbol))
                    symbol = quote.Symbol;

                symbol += umtfSuffix;

                if (stocks.TryGetValue(symbol, out currencyId))
                {
                    if (currencyId == CCY_GBP && !XLON_GBPSymbols.Contains(quote.Symbol))
                        multiplier = 0.01m;
                    else
                        multiplier = 1;
#if !DEBUG
                    LiquidViewHelper.TryImportPriceByUMTF(quote.DateTime.Date, symbol, exchange, (decimal)quote.Open * multiplier, (decimal)quote.High * multiplier, (decimal)quote.Low * multiplier, (decimal)quote.Close * multiplier, (decimal)quote.Close * multiplier, quote.Volume, "EOD Data Web Service", out retVal);
#endif
                    if (retVal == 0)
                        Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", quote.DateTime, exchange, quote.Symbol, (decimal)quote.Close * multiplier);
                }
            }
        }


        private void UpdateQuotesUS(string exchange, QUOTE[] quotes)
        {
            int retVal = -1;
            foreach (QUOTE quote in quotes)
            {
#if !DEBUG
                LiquidViewHelper.TryImportPriceByUSSymbol(quote.DateTime.Date, quote.Symbol, exchange, (decimal)quote.Open, (decimal)quote.High, (decimal)quote.Low, (decimal)quote.Close, (decimal)quote.Close, quote.Volume, "EOD Data Web Service", out retVal);
#endif
                if (retVal == 0)
                    Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", quote.DateTime, exchange, quote.Symbol, quote.Close);

            }
        }


        private void UpdateQuotesUSOptions(string mic, string eodExchange)
        {
            int retVal = -1;
            List<string> symbols;
            if (LiquidViewHelper.TryGetOptionsSymbols(DateTime.Now.AddDays(-1), "USD", out symbols))
            {
                foreach (string symbol in symbols)
                {
                    var response = _soap.QuoteGet(_loginToken, eodExchange, symbol);
                    if (response.QUOTE != null)
                    {
                        var quote = response.QUOTE;
#if !DEBUG
                        LiquidViewHelper.TryImportPriceByOptionSymbol(quote.DateTime.Date, quote.Symbol, "USD", mic, (decimal)quote.Open, (decimal)quote.High, (decimal)quote.Low, (decimal)quote.Close, (decimal)quote.Close, quote.Volume, "EOD Data Web Service", out retVal);
#endif
                        if (retVal == 0)
                            Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", quote.DateTime, mic, quote.Symbol, quote.Close);

                    }
                }
            }
        }

        private void UpdateQuotesUSOptions(string mic, string eodExchange, DateTime date)
        {
            int retVal = -1;
            List<string> symbols;
            if (LiquidViewHelper.TryGetOptionsSymbols(DateTime.Now.AddDays(-1), "USD", out symbols))
            {
                foreach (string symbol in symbols)
                {
                    var response = _soap.SymbolHistoryPeriod(_loginToken, eodExchange, symbol, date.ToString("yyyyMMdd"), "d");
                    
                    if (response.QUOTES != null && response.QUOTES.Length > 0)
                    {
                        var quote = response.QUOTES[0];
#if !DEBUG
                        LiquidViewHelper.TryImportPriceByOptionSymbol(quote.DateTime.Date, quote.Symbol, "USD", mic, (decimal)quote.Open, (decimal)quote.High, (decimal)quote.Low, (decimal)quote.Close, (decimal)quote.Close, quote.Volume, "EOD Data Web Service", out retVal);
#endif
                        if (retVal == 0)
                            Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", quote.DateTime, mic, quote.Symbol, quote.Close);

                    }
                }
            }
        }

        private void UpdateQuotesCanada(string exchange, QUOTE[] quotes)
        {
            int retVal = -1;
            foreach (QUOTE quote in quotes)
            {
#if !DEBUG
                LiquidViewHelper.TryImportPriceByCanadaSymbol(quote.DateTime.Date, quote.Symbol, exchange, (decimal)quote.Open, (decimal)quote.High, (decimal)quote.Low, (decimal)quote.Close, (decimal)quote.Close, quote.Volume, "EOD Data Web Service", out retVal);
#endif
                if (retVal == 0)
                    Console.WriteLine("Success {0:yyyy-MM-dd} {1} {2} {3}", quote.DateTime, exchange, quote.Symbol, quote.Close);

            }
        }


        public bool TryUpdateForex()
        {
            bool someSuccess = false;
            try
            {
                if (_loginToken != String.Empty)
                {
                    foreach (string baseCCY in FOREX_Symbols)
                    {
                        foreach (string subjectCCY in FOREX_Symbols)
                        {
                            if (baseCCY == subjectCCY)
                                continue;

                            try
                            {
                                // subject currency first and base currency last (base = destination)
                                string ccy = subjectCCY + baseCCY;

                                var response = _soap.SymbolHistoryPeriodByDateRange(_loginToken, "FOREX", ccy, DateTime.Now.AddDays(-1).ToString("yyyyMMdd"), DateTime.Now.ToString("yyyyMMdd"), "d");

                                if (response.QUOTES != null)
                                {
                                    foreach (QUOTE quote in response.QUOTES)
                                    {
#if !DEBUG
                                        if (LiquidViewHelper.TryImportFXrate(quote.DateTime.Date, baseCCY, subjectCCY, (decimal)quote.Close))
                                            Console.WriteLine("Success: {3:yyyy-MM-dd} {0} from {1} {2}", baseCCY, subjectCCY, quote.Close, quote.DateTime.Date);
                                        else
                                            Console.WriteLine("Fail:    {3:yyyy-MM-dd} {0} from {1} {2}", baseCCY, subjectCCY, quote.Close, quote.DateTime.Date);
#endif
                                    }
                                }
                                someSuccess = true;
                            }
                            catch
                            { }
                        }
                    }
                    return someSuccess;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("TryUpdateForex caught an exception:{0}\n{1}", e.Message, e.StackTrace);
            }
            return false;
        }


        public bool TryGetEODExchange(MarketDataConstants.ExchangeMICS mic, out string exchange)
        {
            switch(mic)
            {
                case MarketDataConstants.ExchangeMICS.XLON:
                    exchange = "LSE";
                    return true;
                case MarketDataConstants.ExchangeMICS.XMIL:
                    exchange = "MLSE";
                    return true;
                case MarketDataConstants.ExchangeMICS.XNYS:
                    exchange = "NYSE";
                    return true;
                case MarketDataConstants.ExchangeMICS.XNAS:
                    exchange = "NASDAQ";
                    return true;
                case MarketDataConstants.ExchangeMICS.ARCX:
                    exchange = "AMEX";
                    return true;
                case MarketDataConstants.ExchangeMICS.XISX:
                    exchange = "OPRA";
                    return true;
                case MarketDataConstants.ExchangeMICS.XTSE:
                    exchange = "TSX";
                    return true;
                case MarketDataConstants.ExchangeMICS.XTSX:
                    exchange = "TSXV";
                    return true;
                default:
                    exchange = String.Empty;
                    return false;
            }
        }

    }
}
